package com.lean.android.network

import android.util.Log
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import retrofit2.HttpException


object ResultTransformer {

    var customExceptionHandler: ((e: Throwable) -> Result.Error?)? = null

    suspend fun <T> asResult(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Result<T> {
        return asResult(networkCall, successCall, { it }, dispatcher)
    }

    suspend fun <T, R> asResult(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        transformer: (T) -> R,
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Result<R> {
        return withContext(dispatcher) {
            try {
                val response = networkCall()
                if (response.isSuccessful()) {
                    successCall(response)
                    Result.Success(transformer(response.data))
                } else {
                    Result.Error(BusinessException(response.code, response.msg))
                }
            } catch (e: Throwable) {
                handleException(e)
            }
        }
    }

    fun <T, R> asResultFlow(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        transform: (T) -> R,
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Flow<Result<R>> = flow {
        emit(Result.Loading())
        val response = networkCall()
        if (response.isSuccessful()) {
            successCall(response)
            emit(Result.Success(transform(response.data)))
        } else {
            emit(Result.Error(BusinessException(response.code, response.msg)))
        }
    }.flowOn(dispatcher).catch {
        emit(handleException(it))
    }

    /**
     * networkCall ： 使用retrofit请求网络数据
     * successCall : 网络请求成功之后的操作
     * dispatcher ： 在哪个dispatcher执行
     */
    fun <T> asResultFlow(
        networkCall: suspend () -> ApiResponse<T>,
        successCall: suspend (ApiResponse<T>) -> Unit = {},
        dispatcher: CoroutineDispatcher = Dispatchers.IO,
    ): Flow<Result<T>> = flow {
        emit(Result.Loading())
        val response = networkCall()
        if (response.isSuccessful()) {
            successCall(response)
            emit(Result.Success(response.data))
        } else {
            emit(Result.Error(BusinessException(response.code, response.msg)))
        }
    }.flowOn(dispatcher).catch {
        emit(handleException(it))
    }

    @OptIn(ExperimentalSerializationApi::class)
    private fun handleException(e: Throwable): Result.Error {
        Log.e("Http", "Http Error.", e)
        val customHandlerResult = customExceptionHandler?.invoke(e)
        if (customHandlerResult != null) {
            return customHandlerResult
        } else {
            if (e is HttpException) {
                val errorBody = e.response()?.errorBody()
                if (errorBody != null) {
                    try {
                        val errorResponse =
                            JsonFactory.json.decodeFromStream<ApiResponse<Any>>(errorBody.byteStream())
                        return Result.Error(
                            BusinessException(errorResponse.code, errorResponse.msg)
                        )
                    } catch (e: Exception) {
                        return Result.Error(
                            BusinessException(Result.Error.ERROR_BUSINESS, e.message)
                        )
                    }
                } else {
                    return Result.Error(BusinessException(Result.Error.ERROR_BUSINESS, e.message))
                }

            } else {
                return Result.Error(BusinessException(Result.Error.ERROR_BUSINESS, e.message))
            }
        }

    }
}